<?php
/**
 * com_easiv2
 * 
 * @author Lukas Gruber, Internet Technology Kapfenberg, FH JOANNEUN
 * @copyright 2011, Lukas Gruber, Internet Technology Kapfenberg, FH JOANNEUM
 * @license GNU/GPLv3 - http://www.gnu.org/licenses/gpl-3.0.html
 * @contact lukasgruber@gmx.net
 * @website http://www.lukasg255.bplaced.net
 */

defined('_JEXEC') or die();

jimport('joomla.installer.helper');
jimport('joomla.installer.installer');
require_once JPATH_COMPONENT.DS.'helpers'.DS.'xmlparser.php';
require_once JPATH_COMPONENT.DS.'helpers'.DS.'ftphelper.php';

class ExtensionsHelper {

	var $repoDir;
	var $db;
	var $parser;
	var $installer;
	var $extractdir;
	var $xmlFile;
	var $extensionName;
	var $extensionNames = array();
	var $counter = 0;
	var $replaceString;
	var $selectedPackages;
	var $remotePackageName;
	var $instanceName;

	/**
	 * Constructor which initializes important objects and sets a replace String. This String is used to save the 
	 * packgefile path into the database. There the directory seperator is replaced by the replace string to ensure 
	 * operating system independance.
	 * 
	 * @param $repoDir
	 */
	function __construct($repoDir) {

		$this->repoDir = $repoDir;
		$this->db = JFactory::getDBO();
		$this->parser = new XmlParser();

		$this->installer = new JInstaller(); //JInstaller starts the actual installation process

		$this->replaceString = "|";
	}

	/**
	 * Function to set the packages selected by the user from the view
	 * 
	 * @param Array with selected packages
	 */
	function setSelectedPackages($selected) {
		$this->selectedPackages = $selected;
	}

	/**
	 * Function to unpack the packagefile and get the name of the extension
	 *
	 * @param $packagepath
	 * @return Name of the Extension
	 */
	function unpack($packagepath) {

		/*
		 * Check with the easi table, if an entry for the package is set. If not, unpack the package and store data in the easiv2 table.
		 */
		$this->db->setQuery("SELECT packagefile FROM #__easiv2 WHERE packagefile='".str_ireplace(DS, $this->replaceString, $packagepath)."'");
		$result = $this->db->loadObject();

		// unpack if not in the database
		if(count($result) == 0)
		{
			$foldername = JInstallerHelper::unpack($packagepath); //$helper->unpack() returns an array with two fields in it: 'extractdir' and 'packagefile'

			$this->extractdir = $foldername['extractdir'];

			$this->saveManifest();

			/*
			 * Extensions for Joomla 1.5 have 'install' as root element in the XML manifest. 1.6 has 'extension' as root element.
			 */
			$extensionName = $this->parser->getNode(JPATH_COMPONENT.DS.$this->repoDir.DS."manifest".DS.$this->xmlFile[0], "/install/name");
			$version = $this->parser->getNode(JPATH_COMPONENT.DS.$this->repoDir.DS."manifest".DS.$this->xmlFile[0], "/install/version");
			
			if($extensionName == "noNode") {
				$extensionName = $this->parser->getNode(JPATH_COMPONENT.DS.$this->repoDir.DS."manifest".DS.$this->xmlFile[0], "/extension/name");
			}
			
			if($version == "noNode") {
				$version = $this->parser->getNode(JPATH_COMPONENT.DS.$this->repoDir.DS."manifest".DS.$this->xmlFile[0], "/extension/version");
			}
			
			$type = $this->parser->getExtensionType(JPATH_COMPONENT.DS.$this->repoDir.DS.'manifest'.DS.$this->xmlFile[0]);
			
			$this->extensionName = $extensionName;
			
			$this->db->setQuery("INSERT INTO #__easiv2(extension, type, version, packagefile, xmlFile) VALUES ('".$extensionName."', '".$type."', '".$version."', '".str_ireplace(DS, $this->replaceString, $packagepath)."', '".$this->xmlFile[0]."')");
			$this->db->query();

			JFolder::delete($this->extractdir);
		}
		else {
			$this->db->setQuery("SELECT extension FROM #__easiv2 WHERE packagefile='".str_ireplace(DS, $this->replaceString, $packagepath)."'");
			$result = $this->db->loadObject();

			$this->extensionName = $result->extension;
		}
		
		return $this->extensionName;
	}

	function getExtensionName() {
		return $this->extensionName;
	}

	/**
	 * Function to save the xml manifest file of each extension for possible further use
	 */
	function saveManifest() {

		if(!JFolder::exists(JPATH_COMPONENT.DS.$this->repoDir.DS.'manifest')) {
			JFolder::create(JPATH_COMPONENT.DS.$this->repoDir.DS.'manifest');
		}

		//Folder in which the manifest file has to be
		$folderForXml = JFolder::folders($this->extractdir);

		/*
		 * Most extensions have one folder after the unpacking, usually named as the extension itself. In this folder we can find the xml file.
		 * Some, like the Phoca Guestbook, have no folder with it's name. They have the whole MVC structure after unpacking.
		 * So the xml has to be found in the unpacked folder.
		 */
		if(count($folderForXml) > 1) //If more than one folder after unpacking is found, the xml file is in the unpack folder itself
		{
			$this->xmlFile = JFolder::files($this->extractdir, ".xml");

			/*
			 * Some extensions have additional xml files, like config.xml. These are not the manifest files, so we "filter" them out.
			 * We assume that most extension have only one config.xml, so we simply check, if the file does not equal config.xml
			 */
			if(count($this->xmlFile > 1))
			{
				for($j = 0; $j<count($this->xmlFile); $j++)
				{
					if($this->xmlFile[$j] != "config.xml")
					{
						$xmlFileReal = $this->xmlFile[$j];
					}
				}

				JFile::copy($this->extractdir.DS.$xmlFileReal, JPATH_COMPONENT.DS.$this->repoDir.DS."manifest".DS.$xmlFileReal);
				$this->xmlFile[0] = $xmlFileReal;
			}
			else
			{
				JFile::copy($this->extractdir.DS.$this->xmlFile[0], JPATH_COMPONENT.DS.$this->repoDir.DS."manifest".DS.$xmlFile[0]);
			}
		}
		else
		{
			$this->xmlFile = JFolder::files($this->extractdir, ".xml");

			if(count($this->xmlFile) == 0)
			{
				$this->xmlFile = JFolder::files($this->extractdir.DS.$folderForXml[0], ".xml");
				JFile::copy($this->extractdir.DS.$folderForXml[0].DS.$this->xmlFile[0], JPATH_COMPONENT.DS.$this->repoDir.DS."manifest".DS.$this->xmlFile[0]);
			}
			else
			{
				$this->xmlFile = JFolder::files($this->extractdir, ".xml");
				JFile::copy($this->extractdir.DS.$this->xmlFile[0], JPATH_COMPONENT.DS.$this->repoDir.DS."manifest".DS.$this->xmlFile[0]);
			}
		}
	}

	/**
	 * Function to check if extension is installed.
	 * 
	 * @param Extension name
	 * @return true or false
	 */
	function isInstalled($extensionName)
	{
		$this->db->setQuery("SELECT name, manifest_cache FROM #__extensions WHERE name = '".$extensionName."'");

		$result = $this->db->loadObject();
		
		$version = @$this->extractVersionInfo($result->manifest_cache);
			
		if(count($result) != 0) {
			return "true:".$version;
		}
		else {
			return "false";
		}
	}

	/**
	 * Installfunction.
	 * 
	 * Checks if local or remote and starts the appropriate install method
	 */
	function install() {

		if(count($this->selectedPackages) > 0) {
			//table for better formatting with image on summary page
			echo "<table width=\"100%\">";
			//Unpack and install
			foreach($this->selectedPackages as $package) {
				/*
				 * The values of the form come in the following format: "ExtensionName:InstanceToInstallIn:InstallStatus"
				 */
				$instructions = split(":", $package);

				$this->instanceName = $instructions[1];

				$this->db->setQuery("SELECT packagefile FROM #__easiv2 WHERE extension = '".$instructions[0]."'");
				$tempPackagefile = $this->db->loadObject()->packagefile;

				$packagefile = str_ireplace($this->replaceString, DS, $tempPackagefile);

				echo "<tr><td colspan=\"2\">".JText::_('COM_EASIV2_EXTENSIONSHELPER_INSTALLSTRING_PART1')."<b> ".$instructions[0]."</b> ".JText::_('COM_EASIV2_EXTENSIONSHELPER_INSTALLSTRING_PART2')."<b> ".$instructions[1]."</b></td></tr>";

				/*
				 * If $instructions[1] equals LOCAL the local installprocedure is startet
				 */
				if($instructions[1] == "LOCAL") {
					if($instructions[2] == "false") {
						//This variable will contain the folders where the InstallerHelper extracts the fils
						$foldername = JInstallerHelper::unpack($packagefile);
	
						$extractdir = $foldername['extractdir'];
						$packagefile = $foldername['packagefile'];
						//Checks, if installation succeeded
						if(@$this->installer->install($extractdir))
						{
							$img_src = "./components/com_easiv2/images/true.png";
							$msg = JText::_('COM_EASIV2_EXTENSIONSHELPER_INSTALLSUCCESSFUL');
						}
						else
						{
							$img_src = "./components/com_easiv2/images/false.png";
							$msg = JText::_('COM_EASIV2_EXTENSIONSHELPER_INSTALLUNSUCCESSFUL');
						}
						
						JFolder::delete($extractdir); //Deletes the extractfolders after the installation
					}
					else {
						$img_src = "./components/com_easiv2/images/true.png";
						$msg = JText::_('COM_EASIV2_EXTENSIONSHELPER_ALREADYINSTALLED');
					}
				}
				else {
					if($instructions[2] == "false") {
						/*
						 * Starting install process with FTP helper and XML-RPC call
						 */
						$ftpHelper = new FtpHelper();
						$ftpHelper->setPackage($packagefile);
	
						$this->remotePackageName = $ftpHelper->transferPackage($instructions[1]);
	
						if($this->remotePackageName != null) {
							$outcome = $this->installRemote();
														
							if($outcome) {								
								$img_src = "./components/com_easiv2/images/true.png";
								$msg = JText::_('COM_EASIV2_EXTENSIONSHELPER_INSTALLSUCCESSFUL');
							}
							else {
								$img_src = "./components/com_easiv2/images/false.png";
								$msg = JText::_('COM_EASIV2_EXTENSIONSHELPER_INSTALLUNSUCCESSFUL');
							}
						}
						else {
							echo "<span style=\"color:#F00;\">".JText::_('COM_EASIV2_EXTENSIONSHELPER_TRANSMISSIONFAILED_PART1')." <b>".$instructions[0]."</b> ".JText::_('COM_EASIV2_EXTENSIONSHELPER_TRANSMISSIONFAILED_PART2')."</span><br>";
							$img_src = "./components/com_easiv2/images/false.png";
							$msg = JText::_('COM_EASIV2_EXTENSIONSHELPER_INSTALLUNSUCCESSFUL');
						}
					}
					else {
						$img_src = "./components/com_easiv2/images/true.png";
						$msg = JText::_('COM_EASIV2_EXTENSIONSHELPER_ALREADYINSTALLED');
					}
				}
				echo "<tr><td width=\"3%\"><img src=\"".$img_src."\" /></td><td valign=\"middle\" align=\"left\"><b>".$msg."</b><br></td></tr>";
			}
			echo "<tr><td colspan=\"2\"><hr></td></tr>";
			echo '</table>';
		}
		else {
			echo JText::_('COM_EASIV2_EXTENSIONSHELPER_NOTHINGSELECTED');
		}
	}

	/**
	 * Transfers the packagefiles to the remote instance and triggers the install there via XML-RPC
	 * 
	 * @return true or false
	 */
	function installRemote() {
		$this->db->setQuery("SELECT http_address, identifier FROM #__easiv2_instances WHERE name ='".$this->instanceName."'");
		$instanceUrl = $this->db->loadObject()->http_address;
		$identifier = $this->db->loadObject()->identifier;
		
		$requestMethod = "manageExtensions.install";
		$request_param = xmlrpc_encode(array($this->remotePackageName, $identifier));

		$postdata = http_build_query(array(
				'toInstall' => $request_param,
			)
		);

		$opts = array('http' =>	array(
		        'method'  => 'POST',
		        'header'  => 'Content-type: application/x-www-form-urlencoded',
				'content' => $postdata
			)
		);

		$context  = stream_context_create($opts);

		$response = file_get_contents($instanceUrl."index.php?option=com_easiv2mi&task=".$requestMethod."&format=xmlrpc", false, $context);
		
		if($response == "true") {
			return true;	
		}
		else {
			return false;
		}
	}

	/**
	 * Uninstallfunction
	 * 
	 * Checks if local or remote and triggers the appropriate uninstall method
	 */
	function uninstall()
	{
		if(count($this->selectedPackages) > 0) {
			//Table for better formatting
			echo "<table width=\"100%\">";
	
			foreach($this->selectedPackages as $package) {
				/*
				 * The values of the form come in the following format: "ExtensionName:InstanceToInstallIn:InstallStatus"
				 */
				$instructions = split(":", $package);
	
				$this->instanceName = $instructions[1];
				$this->extensionName = $instructions[0];
	
				$this->db->setQuery("SELECT type FROM #__easiv2 WHERE extension = '".$instructions[0]."'");
				
				/*
				 * Uninstall need informtaion about the type (plugin, module or component)
				 */
				$type = $this->db->loadObject()->type;
	
				echo "<tr><td colspan=\"2\">".JText::_('COM_EASIV2_EXTENSIONSHELPER_UNINSTALLSTRING_PART1')." <b>".$instructions[0]."</b> ".JText::_('COM_EASIV2_EXTENSIONSHELPER_UNINSTALLSTRING_PART2')." <b>".$instructions[1]."</b></td></tr>";
	
				$this->db->setQuery("SELECT extension_id FROM #__extensions WHERE name ='".$instructions[0]."'");
	
				$extensionId = $this->db->loadObject()->extension_id;
					
				/**
				 *	Uninstallation Function.
				 */
				if($instructions[1] == "LOCAL") {
					if($instructions[2] == "true") {
						if(@$this->installer->uninstall($type, $extensionId, 0)) {
							$img_src = "./components/com_easiv2/images/true.png";
							$msg = JText::_('COM_EASIV2_EXTENSIONSHELPER_UNINSTALLSUCCESSFUL');
						}
						else {
							$img_src = "./components/com_easiv2/images/false.png";
							$msg = JText::_('COM_EASIV2_EXTENSIONSHELPER_UNINSTALLUNSUCCESSFUL');
						}
					}
					else {
						$img_src = "./components/com_easiv2/images/false.png";
						$msg = JText::_('COM_EASIV2_EXTENSIONSHELPER_NOTINSTALLED');
					}
				}
				else {
					if($instructions[2] == "true") {
						if($this->uninstallRemote()) {
							$img_src = "./components/com_easiv2/images/true.png";
							$msg = JText::_('COM_EASIV2_EXTENSIONSHELPER_UNINSTALLSUCCESSFUL');
						}
						else {
							$img_src = "./components/com_easiv2/images/false.png";
							$msg = JText::_('COM_EASIV2_EXTENSIONSHELPER_UNINSTALLUNSUCCESSFUL');
						}
					}
					else {
						$img_src = "./components/com_easiv2/images/false.png";
						$msg = JText::_('COM_EASIV2_EXTENSIONSHELPER_NOTINSTALLED');
					}
				}
				
				echo "<tr><td width=\"3%\"><img src=\"".$img_src."\" /></td><td valign=\"middle\" align=\"left\"><b>".$msg."</b><br></td></tr>";
			}
			
			echo "<tr><td colspan=\"2\"><hr></td></tr>";
			echo '</table>';
		}
		else {
			echo JText::_('COM_EASIV2_EXTENSIONSHELPER_NOTHINGSELECTED');
		}
	}

	/**
	 * Uninstallfuction for remote instances via XML-RPC
	 * 
	 * @return true or false
	 */
	function uninstallRemote() {
		$this->db->setQuery("SELECT http_address, identifier FROM #__easiv2_instances WHERE name ='".$this->instanceName."'");
		$instanceUrl = $this->db->loadObject()->http_address;
		$identifier = $this->db->loadObject()->identifier;

		/*
		 * Uninstall need informtaion about the type (plugin, module or component)
		 */
		$this->db->setQuery("SELECT type FROM #__easiv2 WHERE extension = '".$this->extensionName."'");
		$type = $this->db->loadObject()->type;


		$requestMethod = "manageExtensions.uninstall";
		$request_param = xmlrpc_encode($this->remotePackageName);

		$params = array($this->extensionName, $type, $identifier);

		$request_params = xmlrpc_encode($params);


		$postdata = http_build_query(array(
        		'information' => $request_params,
			)
		);

		$opts = array('http' =>	array(
		        'method'  => 'POST',
		        'header'  => 'Content-type: application/x-www-form-urlencoded',
		        'content' => $postdata
			)
		);

		$context  = stream_context_create($opts);
		
		$response = file_get_contents($instanceUrl."index.php?option=com_easiv2mi&task=".$requestMethod."&format=xmlrpc", false, $context);

		if($response == "true") {
			return true;
		}
		else {
			return false;
		}
	}
	
	/**
	 * To get to the version info the content of the manifest_cache attribute of the #__extensions table is read.
	 */
	function extractVersionInfo($mc) {
		/*
		 * the first appearance of the word version is discovered. The result looks like this:
		 * 
		 * version":"1.5.0","description":"It counts the shopping entries","group":""}
		 * 
		 * Next, the position of the ':'-seperator charakter is discovered
		 * 
		 * The result looks like this:
		 */
		$temp = stristr($mc, 'version');
		$seperatorPos = strpos($temp, ':');
		
		/*
		 * Next, the string from the startposition of $temp to the found seperator position is stored in $temp again
		 * 
		 * $temp would now look like this:
		 * 
		 * :"1.5.0","description":"It counts the shopping entries","group":""}
		 */
		$temp = substr($temp, $seperatorPos);
		
		/*
		 * Next, the ','-seperator position is dicovered
		 */
		$seperatorPos = strpos($temp, ',');
		
		/*
		 * Now the version can be read with the following command
		 */
		$version = substr($temp, 2, $seperatorPos-3);
		
		return $version;
	}
}